// CH6_4.cpp
// by Charles Mc Auley
// "Programming AutoCAD with ObjectARX"
//
//	This application is a redo of the Window application
//	of Chapter 5. This version of the application shows
//	the mechanism of nesting DCL dialog boxs and is essentially
//  an extension of the application Ch6_3.arx
//
/////////////////////////////////////////////////////////


#include <acdb.h>		// acdb definitions
#include <adslib.h>		// ads defs
#include <rxregsvc.h>	// unlock application
#include <aced.h>
#include <dbsymtb.h>	// Symbol Tables
#include <adsdlg.h>		// DCL style dialogs
#include <geassign.h>	// asDblArray
#include <dbapserv.h>	// acdbHostApplicationServices()

#include <stdio.h>		// for sprintf

// TODO: add your own includes
#include "RectWindow.h"	// RectWindow class
#include "ArchWindow.h"	// ArchWindow class
#include "ApexWindow.h"	// ApexWindow Class


// Function prototypes
// entry point for this application
extern "C" AcRx::AppRetCode acrxEntryPoint( AcRx::AppMsgCode msg, void* );

// helper functions
void initApp  (void);
void unloadApp(void);

// user defined functions
void windo();
Adesk::Boolean getModelSpaceRecord(AcDbBlockTableRecord*& pBlkTableRecord);


// Dialog call back function prototypes
static void CALLB dlgOkBtnCB(ads_callback_packet *cpkt);
static void CALLB dlgPickPtBtnCB(ads_callback_packet *cpkt);
static void CALLB dlgWinTypeRbCB(ads_callback_packet *cpkt);
static void CALLB dlgEbHeightCB(ads_callback_packet *cpkt);
static void CALLB dlgEbWidthCB(ads_callback_packet *cpkt);
static void CALLB dlgEbColsCB(ads_callback_packet *cpkt);
static void CALLB dlgEbRowsCB(ads_callback_packet *cpkt);
static void CALLB dlgEbXValCB(ads_callback_packet *cpkt);
static void CALLB dlgEbYValCB(ads_callback_packet *cpkt);
static void CALLB dlgStatsBtnCB(ads_callback_packet *cpkt);

// Global variables, place holders for the values
// obtained from the dialog box

double	g_winHt = 20.00;
double	g_winWidth = 20.00;
int		g_Rows = 1;
int		g_Cols = 1;
char	g_strWinType[10] = {"rb_rect"};
AcGePoint2d g_startPt(0.0, 0.0);
Adesk::Boolean g_bProceed = Adesk::kFalse;


/////////////////////////////////////////////////////////////////////
// acrxEntryPoint(internal)
// This function is the entry point for your application.
/////////////////////////////////////////////////////////////////////
AcRx::AppRetCode acrxEntryPoint(AcRx::AppMsgCode msg, void* ptr)
{
	switch (msg) {
		case AcRx::kInitAppMsg :
			acrxUnlockApplication(ptr);
			acrxRegisterAppNotMDIAware(ptr);
			initApp();
		break;

		case AcRx::kUnloadAppMsg :
			unloadApp();
		break;

		case AcRx::kLoadDwgMsg :
		break;

		case AcRx::kUnloadDwgMsg :
		break;

		default:
		break;
	} // switch

  return AcRx::kRetOK;
}

void initApp(void)
{
	// TODO: init your application
    // register a command with the AutoCAD command mechanism

    acedRegCmds->addCommand("CH6_APPS", "WINDO", "WINDO",
                    ACRX_CMD_MODAL, windo);
	acutPrintf("Enter \"WINDO\" at the command prompt to draw a window.\n");

}

void unloadApp(void)
{
	// TODO: clean up your application
    // Remove the command group added via acedRegCmds->addCommand

	acutPrintf("%s%s", "Goodbye\n", "Removing command group \"CH6_APPS\"\n");
    acedRegCmds->removeGroup("CH6_APPS");

}

// TODO: add your other functions here

void windo()
{

	int rc; // Return code
	char dclFile[TILE_STR_LIMIT];	// TILE_STR_LIMIT = 255 in adsdlg.h file
	char dclName[80];
	int dclId, dbStat;				// dcl ID and dialog box status
	ads_hdlg dlgHdl;				// Dialog Box handle


	AcDbBlockTableRecord *pBlkTableRecord;

	RectWindow *pRectWindow;	// RectWindow class
	ArchWindow *pArchWindow;	// Derived ArchWindow class
	ApexWindow *pApexWindow;	// Derived ApexWindow class
	RectWindow *pGenericWind;

	AcGePoint3d pickPt;			// Lower left point of the window

	char buffer[80];	// Used in the conversion of numbers to string


	dbStat = 5;	// Set an initial value for dbStat must be greater than 1

	strcpy(dclFile, "ch6_4.dcl");
	strcpy(dclName, "ch6_4");
	
	rc = ads_load_dialog(dclFile, &dclId);
	if(rc != RTNORM)
	{
		acutPrintf("%s %s", "\nError loading Ch6_4.DCL file. ", dclFile);
		return;
	}


	while(dbStat >= DLGSTATUS)
	{
		// Now that the dialog is loaded, display it
		rc = ads_new_dialog(dclName, dclId, NULLCB, &dlgHdl);
		if(rc != RTNORM)
		{
			acutPrintf("\nError displaying dialog. ");
			return;
		}

		// Now that the dialog is visible on the screen
		// initialize it.
		if(strcmp(g_strWinType, "rb_rect") == 0)
		{
			ads_set_tile(dlgHdl, "rb_rect", "1");
		}
		else if(strcmp(g_strWinType, "rb_arch") == 0)
		{
			ads_set_tile(dlgHdl, "rb_arch", "1");
		}
		else if(strcmp(g_strWinType, "rb_apex") == 0)
		{
			ads_set_tile(dlgHdl, "rb_apex", "1");
		}
		else
		{
			ads_set_tile(dlgHdl, "eb_height", "20.00");
			ads_set_tile(dlgHdl, "eb_width", "20.00");
		}

		// Convert the doubles for the height and width
		acdbRToS(g_winHt, 2, 3, buffer);
		ads_set_tile(dlgHdl, "eb_height", buffer);
		acdbRToS(g_winWidth, 2, 3, buffer);
		ads_set_tile(dlgHdl, "eb_width", buffer);
		
		// Convert the integers for the rows and columns
		_itoa(g_Cols, buffer, 10);
		ads_set_tile(dlgHdl, "eb_cols", buffer);
		_itoa(g_Rows, buffer, 10);
		ads_set_tile(dlgHdl, "eb_rows", buffer);
		acdbRToS((ads_real)g_startPt.x, 2, 3, buffer);
		ads_set_tile(dlgHdl, "eb_xval", buffer);
		acdbRToS((ads_real)g_startPt.y, 2, 3, buffer);
		rc = ads_set_tile(dlgHdl, "eb_yval", buffer);


		ads_action_tile(dlgHdl, "accept", dlgOkBtnCB);
		ads_action_tile(dlgHdl, "btn_pick", dlgPickPtBtnCB);

		ads_action_tile(dlgHdl, "rb_rect", dlgWinTypeRbCB);
		ads_action_tile(dlgHdl, "rb_arch", dlgWinTypeRbCB);
		ads_action_tile(dlgHdl, "rb_apex", dlgWinTypeRbCB);

		ads_action_tile(dlgHdl, "eb_height", dlgEbHeightCB);
		ads_action_tile(dlgHdl, "eb_width", dlgEbWidthCB);
		ads_action_tile(dlgHdl, "eb_cols", dlgEbColsCB);
		ads_action_tile(dlgHdl, "eb_rows", dlgEbRowsCB);
		ads_action_tile(dlgHdl, "eb_xval", dlgEbXValCB);
		ads_action_tile(dlgHdl, "eb_yval", dlgEbYValCB);

		// The action tile to launch the nested dialog
		ads_action_tile(dlgHdl, "btn_stats", dlgStatsBtnCB);


		rc = ads_start_dialog(dlgHdl, &dbStat);
		if(rc != RTNORM)
		{
			acutPrintf("\nFailed to initialize the dialog box. ");
			return;
		}

		switch(dbStat)
		{
			case 4:
				acedInitGet(NULL, NULL);
				rc = acedGetPoint(NULL,
						"\nPick lower left window insertion point <Return for (0,0)>: ",
						asDblArray(pickPt));

				switch(rc)
				{
					case RTCAN:
					case RTERROR:
						return;

					case RTNONE:
						g_startPt.x = 0;
						g_startPt.y = 0;
					break;

					case RTNORM:
						g_startPt.x = pickPt.x;
						g_startPt.y = pickPt.y;
					break;
				} // inner switch

			break;

		} // switch

	} // while

	
	// Unload the dialog box
	ads_unload_dialog(dclId);

	if(g_bProceed)
	{
		// What kind of window did the user select?
		if(strcmp(g_strWinType, "rb_rect") == 0)
		{
			pRectWindow = new RectWindow;
			pGenericWind = pRectWindow;
		}
		else if(strcmp(g_strWinType, "rb_arch") == 0)
		{
			pArchWindow = new ArchWindow;
			pGenericWind = pArchWindow;
		}
		else if(strcmp(g_strWinType, "rb_apex") == 0)
		{
			pApexWindow = new ApexWindow;
			pGenericWind = pApexWindow;
		}
		else
		{
			return;
		}

		// Fill out the window class object
		// with the input values

		pGenericWind->setWindowLength(g_winWidth);
		pGenericWind->setWindowHeight(g_winHt);
		pGenericWind->setWindowCols(g_Cols);
		pGenericWind->setWindowRows(g_Rows);
		pGenericWind->setWindowStartPoint(g_startPt);

		if(!getModelSpaceRecord(pBlkTableRecord))
		{
			return;
		}

		pGenericWind->drawWindow(pBlkTableRecord);

		if(pGenericWind != NULL)
		{
			delete pGenericWind;
		}

		pBlkTableRecord->close();
		
		// Set the boolean back to false, because
		// in the next invocation of the application
		// AutoCAD remembers the values of the global
		// variables.
		g_bProceed = Adesk::kFalse;

	}// if g_bProceed
	else
	{
		acutPrintf("\nUser cancelled. ");
	}
}

// Dialog Box callback functions

static void CALLB dlgOkBtnCB(ads_callback_packet *cpkt)
{
	char strWinHt[100];
	char strWinWidth[100];
	char strWinRows[20];
	char strWinCols[20];
	char strWinType[10];
	char strXPt[20];
	char strYPt[20];

	int rc;
	double winHt, winWidth;
	double xPt, yPt;
	int winRows, winCols;

	
	// Get the window height
	rc = ads_get_tile(cpkt->dialog, "eb_height", strWinHt, 80);
	if(rc != RTNORM)
	{
		ads_done_dialog(cpkt->dialog, DLGOK);
		acutPrintf("\nError in Height edit box. ");
		return;
	}
	winHt = atof(strWinHt);
	if(winHt <= 0.0)
	{
		ads_done_dialog(cpkt->dialog, DLGOK);
		acutPrintf("\nInvalid value for window Height. ");
		return;
	}

	// Get the window width
	rc = ads_get_tile(cpkt->dialog, "eb_width", strWinWidth, 80);
	if(rc != RTNORM)
	{
		ads_done_dialog(cpkt->dialog, DLGOK);
		acutPrintf("\nError in Width edit box. ");
		return;
	}
	winWidth = atof(strWinWidth);
	if(winWidth <= 0.0)
	{
		ads_done_dialog(cpkt->dialog, DLGOK);
		acutPrintf("\nInvalid value for window width. ");
		return;
	}

	// Get the number of window columns
	rc = ads_get_tile(cpkt->dialog, "eb_cols", strWinCols, 20);
	if(rc != RTNORM)
	{
		ads_done_dialog(cpkt->dialog, DLGOK);
		acutPrintf("\nError in Columns edit box. ");
		return;
	}
	winCols = atoi(strWinCols);
	if(winCols < 1)
	{
		acedAlert("Invalid value for window columns.\nMinimum value = 1");
		ads_mode_tile(cpkt->dialog, "eb_cols", MODE_SETFOCUS);
	}

	// Get the number of window rows
	rc = ads_get_tile(cpkt->dialog, "eb_rows", strWinRows, 20);
	if(rc != RTNORM)
	{
		ads_done_dialog(cpkt->dialog, DLGOK);
		acutPrintf("\nError in Rows edit box. ");
		return;
	}
	winRows = atoi(strWinRows);
	if(winRows < 1)
	{
		acedAlert("Invalid value for window rows.\nMinimum value = 1");
		ads_mode_tile(cpkt->dialog, "eb_rows", MODE_SETFOCUS);
	}

	// The following ads_get_tile() call will place in strWinType
	// the key of the radio button that is selected in other words
	// the radio button that has a value of "1". The key brc_wintype
	// is a boxed_radio_column tile that contains radio_button tiles.

	rc = ads_get_tile(cpkt->dialog, "brc_wintype", strWinType, 10);
	if(rc != RTNORM)
	{
		ads_done_dialog(cpkt->dialog, DLGOK);
		acutPrintf("\nError in window type. ");
		return;
	}

	// Get the Window starting x and y values
	rc = ads_get_tile(cpkt->dialog, "eb_xval", strXPt, 20);
	rc = ads_get_tile(cpkt->dialog, "eb_yval", strYPt, 20);

	xPt = atof(strXPt);
	yPt = atof(strYPt);


	ads_done_dialog(cpkt->dialog, DLGOK);

	// Transfer the values obtained to the global
	// variables.

	g_winHt		= winHt;
	g_winWidth	= winWidth;
	g_Cols		= winCols;
	g_Rows		= winRows;

	strcpy(g_strWinType, strWinType);

	g_startPt.set(xPt, yPt);

	g_bProceed = Adesk::kTrue;
}

static void CALLB dlgPickPtBtnCB(ads_callback_packet *cpkt)
{
	ads_done_dialog(cpkt->dialog, 4);
}


static void CALLB dlgWinTypeRbCB(ads_callback_packet *cpkt)
{
	char strWinType[10];
	int rc;

	// The following ads_get_tile() call will place in strWinType
	// the key of the radio button that is selected in other words
	// the radio button that has a value of "1". The key brc_wintype
	// is a boxed_radio_column tile that contains radio_button tiles.

	rc = ads_get_tile(cpkt->dialog, "brc_wintype", strWinType, 10);
	if(rc != RTNORM)
	{
		ads_done_dialog(cpkt->dialog, DLGOK);
		acutPrintf("\nError in window type. ");
		return;
	}

	strcpy(g_strWinType, strWinType);
	
}

static void CALLB dlgEbHeightCB(ads_callback_packet *cpkt)
{
	int rc;
	char strWinHt[100];
	double winHt;

	if(cpkt->reason == CBR_LOST_FOCUS)
	{
		// Copy the value to the global variable
		// Get the window height
		rc = ads_get_tile(cpkt->dialog, "eb_height", strWinHt, 80);
		if(rc != RTNORM)
		{
			ads_done_dialog(cpkt->dialog, DLGOK);
			acutPrintf("\nError in Height edit box. ");
			return;
		}
		winHt = atof(strWinHt);
		if(winHt <= 0.0)
		{
			acedAlert("Invalid value for window height");
			ads_mode_tile(cpkt->dialog, "eb_height", MODE_SETFOCUS);
		}
		else
		{
			// Copy value out to global
			g_winHt = winHt;
		}
	}
}


static void CALLB dlgEbWidthCB(ads_callback_packet *cpkt)
{
	int rc;
	char strWinWidth[100];
	double winWidth;

	if(cpkt->reason == CBR_LOST_FOCUS)
	{
		// Copy the value to the global variable
		// Get the window height
		rc = ads_get_tile(cpkt->dialog, "eb_width", strWinWidth, 80);
		if(rc != RTNORM)
		{
			ads_done_dialog(cpkt->dialog, DLGOK);
			acutPrintf("\nError in Width edit box. ");
			return;
		}
		winWidth = atof(strWinWidth);
		if(winWidth <= 0.0)
		{
			acedAlert("Invalid value for window width");
			ads_mode_tile(cpkt->dialog, "eb_width", MODE_SETFOCUS);
		}
		else
		{
			// Copy value out to global
			g_winWidth = winWidth;
		}
	}
}

static void CALLB dlgEbColsCB(ads_callback_packet *cpkt)
{
	int rc;
	char strWinCols[20];
	int winCols;

	if(cpkt->reason == CBR_LOST_FOCUS)
	{
		// Copy the value to the global variable
		// Get the window height
		rc = ads_get_tile(cpkt->dialog, "eb_cols", strWinCols, 20);
		if(rc != RTNORM)
		{
			ads_done_dialog(cpkt->dialog, DLGOK);
			acutPrintf("\nError in Columns edit box. ");
			return;
		}
		winCols = atoi(strWinCols);
		if(winCols < 1)
		{
			acedAlert("Invalid value for window columns.\nMimimum = 1");
			ads_mode_tile(cpkt->dialog, "eb_cols", MODE_SETFOCUS);
		}
		else
		{
			// Copy value out to global
			g_Cols = winCols;
		}
	}
}


static void CALLB dlgEbRowsCB(ads_callback_packet *cpkt)
{
	int rc;
	char strWinRows[20];
	int winRows;

	if(cpkt->reason == CBR_LOST_FOCUS)
	{
		// Copy the value to the global variable
		// Get the window height
		rc = ads_get_tile(cpkt->dialog, "eb_rows", strWinRows, 20);
		if(rc != RTNORM)
		{
			ads_done_dialog(cpkt->dialog, DLGOK);
			acutPrintf("\nError in Rows edit box. ");
			return;
		}
		winRows = atoi(strWinRows);
		if(winRows < 1)
		{
			acedAlert("Invalid value for window rows.\nMimimum = 1");
			ads_mode_tile(cpkt->dialog, "eb_rows", MODE_SETFOCUS);
		}
		else
		{
			// Copy value out to global
			g_Rows = winRows;
		}
	}
}

static void CALLB dlgEbXValCB(ads_callback_packet *cpkt)
{
	int rc;
	char strXPt[20];
	double xPt;

	if(cpkt->reason == CBR_LOST_FOCUS)
	{
		// Copy the value to the global variable
		// Get the window height
		rc = ads_get_tile(cpkt->dialog, "eb_xval", strXPt, 20);
		if(rc != RTNORM)
		{
			ads_done_dialog(cpkt->dialog, DLGOK);
			acutPrintf("\nError in X Value edit box. ");
			return;
		}

		xPt = atof(strXPt);
		
		g_startPt.x = xPt;
	}
}


static void CALLB dlgEbYValCB(ads_callback_packet *cpkt)
{
	int rc;
	char strYPt[20];
	double yPt;

	if(cpkt->reason == CBR_LOST_FOCUS)
	{
		// Copy the value to the global variable
		// Get the window height
		rc = ads_get_tile(cpkt->dialog, "eb_yval", strYPt, 20);
		if(rc != RTNORM)
		{
			ads_done_dialog(cpkt->dialog, DLGOK);
			acutPrintf("\nError in Y value edit box. ");
			return;
		}

		yPt = atof(strYPt);
		
		g_startPt.y = yPt;
	}
}

// Callback function to load the nested dialog
static void CALLB dlgStatsBtnCB(ads_callback_packet *cpkt)
{
	char sub_dclName[20] = {"stats"};
	char sub_dclFile[TILE_STR_LIMIT] = {"ch6_4.dcl"};
	char str_list[100];
	char strWinType[20];
	int subdclId, subDbStat;
	ads_hdlg subHdl;
	int rc;

	rc = ads_load_dialog(sub_dclFile, &subdclId);
	if(rc != RTNORM)
	{
		acutPrintf("%s %s", "\nError load DCL file", sub_dclFile);
		return;
	}

	rc = ads_new_dialog(sub_dclName, subdclId, NULLCB, &subHdl);
	if(rc != RTNORM)
	{
		acutPrintf("\nError displaying dialog");
		return;
	}

	if(strcmp(g_strWinType, "rb_rect") == 0)
	{
		strcpy(strWinType, "Rect");
	}
	else if(strcmp(g_strWinType, "rb_arch") == 0)
	{
		strcpy(strWinType, "Arch");
	}
	else if(strcmp(g_strWinType, "rb_apex") == 0)
	{
		strcpy(strWinType, "Apex");
	}
	else
	{
		strcpy(strWinType, "--");
	}

	// Initialize the sub dialog here
	sprintf(str_list, "%s   %i\t%i\t%.3lf\t%.3lf",
			strWinType, g_Rows, g_Cols, g_winHt, g_winWidth);

	// Build up the list
	ads_start_list(subHdl, "lb_stats", LIST_NEW, 0);
	ads_add_list(str_list);
	ads_end_list();

	rc = ads_start_dialog(subHdl, &subDbStat);
	if(rc != RTNORM)
	{
		acutPrintf("\nFailed to initialize the dialog box.");
		return;
	}

	ads_unload_dialog(subdclId);
}


// User defined functions

Adesk::Boolean getModelSpaceRecord(AcDbBlockTableRecord*& pBlkTableRecord)
{
	AcDbDatabase *pCurDb;
	AcDbBlockTable *pBlkTable;
	Acad::ErrorStatus es;

	pCurDb = acdbHostApplicationServices()->workingDatabase();

	es = pCurDb->getBlockTable(pBlkTable, AcDb::kForRead);
	if(es != Acad::eOk)
	{
		acutPrintf("\nFailed to open Block Table for a read operation.");
		return Adesk::kFalse;
	}

	es = pBlkTable->getAt(ACDB_MODEL_SPACE, pBlkTableRecord, AcDb::kForWrite);
	if(es != Acad::eOk)
	{
		acutPrintf("\nFailed to open MODEL SPACE for a write operation.");
		pBlkTable->close();
		return Adesk::kFalse;
	}

	// We don't need the block table anymore so we can close it.
	pBlkTable->close();

	return Adesk::kTrue;
}